#
# Option to specify compiler prefix
# e.g. --cross-prefix=arm-linux
#      --cross-prefix=mips-linux
#
AddOption('--cross-prefix',
          dest='cross_prefix',
          type='string',
          nargs=1,
          action='store',
          help='build with a cross compiler')

#
# Option which will build in debug mode
#
AddOption('--bdbg',
          dest='builddebug',
          action='store_true',
          help='build with debugging information')

#
# Option to build cppflib as a shared library (so) instead of a static one
#
AddOption('--buildso',
          dest='buildso',
          action='store_true',
          help='build shared library version of cppflib')

def GetBuildingEnvironment() :
   buildenv = Environment()
   CROSS_PREFIX = GetOption('cross_prefix')
   if (CROSS_PREFIX):
      import os
      buildenv['ENV'] = {'PATH' : os.environ['PATH']}  # bring the shell PATH to this script
      buildenv['CC'] = CROSS_PREFIX + '-gcc'
      buildenv['CXX'] = CROSS_PREFIX + '-g++'
      buildenv['AR'] = CROSS_PREFIX + '-ar'
      buildenv['AS'] = CROSS_PREFIX + '-as'
      buildenv['RANLIB'] = CROSS_PREFIX + '-ranlib'
   return buildenv

# to make wchar_t 16 bit little endian (-fshort_wchar)
def GetBuildCCFlags() :
   flagslist = ['-DUNICODE', '-D_UNICODE', '-fshort-wchar']
   if GetOption('buildso'):
      flagslist.append('-DUSE_CPPFLIBSO')
   if GetOption('builddebug'):
      flagslist.append(['-D_DEBUG', '-DDEBUG', '-g'])
   else:
      flagslist.append('-O2')
   CROSS_PREFIX = GetOption('cross_prefix')
   if (CROSS_PREFIX and CROSS_PREFIX.find("android") >= 0):  # building for android
      flagslist.append(['-mthumb', '-Wl,--fix-cortex-a8'])
   return flagslist

def GetOutputDir(orgDir) :
   outDir = orgDir
   if GetOption('buildso'):
      outDir += '_so'
   CROSS_PREFIX = GetOption('cross_prefix')
   if (CROSS_PREFIX):
      outDir += '_' + CROSS_PREFIX.replace('-', '_')
   if GetOption('builddebug'):
      outDir += '_debug'
   return outDir

def GetPackagePath(package) :
   platform = 'linux'
   CROSS_PREFIX = GetOption('cross_prefix')
   if (CROSS_PREFIX):
      platform = CROSS_PREFIX
   debugOrRel = 'rel'
   if GetOption('builddebug'):
      debugOrRel = 'rel'
   return '../extlib/' + package + '/' + platform + '/lib/' + debugOrRel
	  
def GetExtraLibraryPath() :
   return [GetPackagePath('openssl'), GetPackagePath('zlib')]

def GetExtraObjectFiles() :
   dirlist = ['../extlib/sqlite/']
   extraobjs = ['sqlite3.o']
   if GetOption('buildso'):
      extraobjs = ['sqlite3.os']
   config = '/lib/rel/'
   if GetOption('builddebug'):
      config = '/lib/debug/'
   platform = 'linux'
   CROSS_PREFIX = GetOption('cross_prefix')
   if (CROSS_PREFIX):
      platform = CROSS_PREFIX
   retlist = []
   for i in range(len(extraobjs)):
      retlist.append(dirlist[i] + platform + config + extraobjs[i])
   return retlist
   

def GetLinkingLibrariesForExe() :
   linkLibs = ['cppflib', 'crypto_m', 'ssl_m', 'z']
   CROSS_PREFIX = GetOption('cross_prefix')
   if (CROSS_PREFIX and CROSS_PREFIX.find("android") >= 0):  
      linkLibs.extend(['log', 'supc++'])                      # linking libraries for android (__cxa_throw, std::uncaught_exception, etc, ...)
   else:
      linkLibs.extend(['pthread', 'dl'])    # android doesn't need 'pthread' as it is implemented in libc.so
   return linkLibs


def GetLinkingLibrariesForCppflib() :
   linkLibs = []                      
   # For normal Linux, it is not necessary. Only the final exe need to link the necessary shared objects.
   #
   # On Android, one must explicitly link these libraries when building .so or .a, which is different from normal Linux,
   # because for Android 2.0 and later, the system behaves as if dlopen(RTLD_LOCAL) is always in effect.
   # 
   # 'libsupc++' is copied from /sources/cxx-stl/gnu-libstdc++/libs/armeabi
   #
   CROSS_PREFIX = GetOption('cross_prefix')
   if (CROSS_PREFIX and CROSS_PREFIX.find("android") >= 0):  
      linkLibs = ['crypto_m', 'ssl_m', 'supc++', 'log', 'z']          
   return linkLibs


def BuildLibrary(env, target, objs) :
   if GetOption('buildso'):
      env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1
      return env.SharedLibrary(target, objs)  # build shared library
   return env.StaticLibrary(target, objs)     # build static library

def ShouldGiveWarning() :
   if GetOption('buildso'):
      CROSS_PREFIX = GetOption('cross_prefix')
      if (CROSS_PREFIX and CROSS_PREFIX.find("android") >= 0):
         return True
   return False

def PostBuildWarning(target = None, source = None, env = None): 
    print '*********************************************************************************'
    print ' Warning: Shared object version of cppflib on Android will crash due to the bug'
    print '          of "dynmaic_cast" when comparing class names across shared object '
    print '          boundaries'
    print '*********************************************************************************'
    return 0

def PostCopySharedObject(target = None, source = None, env = None): 
    import shutil
    tDir = str(target[0])
    index = tDir.find('/')
    if (index >= 0):
       tDir = tDir[:index]
    print '*********************************************************************'
    print '* Copy "libcppflib.so" to "' + tDir + '"'
    print '*********************************************************************'
    shutil.copy(GetOutputDir('bin_cppflib') + '/libcppflib.so', tDir)
    return 0

##########################
# build cppflib
##########################
CPPFLIBOUT = GetOutputDir('bin_cppflib')
CPPFLIBSRCBASE = '../cppflib'

cppflibEnv = GetBuildingEnvironment()
cppflibEnv.VariantDir(CPPFLIBOUT, CPPFLIBSRCBASE, duplicate = 0)
cppflibEnv.Append(CCFLAGS = GetBuildCCFlags())
cppflibEnv.Prepend(CPPPATH = ['../extlib/openssl/include', '../extlib/openssl/linux/include', '../extlib/sqlite/include', '../extlib/zlib/include'])
cppflibEnv.Append(LIBS = GetLinkingLibrariesForCppflib())
cppflibEnv.Prepend(LIBPATH = GetExtraLibraryPath())

flibObjs = Glob(CPPFLIBOUT + '/common/__internal/*.c')
flibObjs.extend(Glob(CPPFLIBOUT + '/common/*.c'))
flibObjs.extend(Glob(CPPFLIBOUT + '/common/*.cpp'))
flibObjs.extend(Glob(CPPFLIBOUT + '/text/*.cpp'))
flibObjs.extend(Glob(CPPFLIBOUT + '/collections/*.cpp'))
flibObjs.extend(Glob(CPPFLIBOUT + '/collections/tree/__internal/*.cpp'))
flibObjs.extend(Glob(CPPFLIBOUT + '/collections/tree/*.cpp'))
flibObjs.extend(Glob(CPPFLIBOUT + '/collections/sort/*.cpp'))
flibObjs.extend(Glob(CPPFLIBOUT + '/exceptions/*.cpp'))
flibObjs.extend(Glob(CPPFLIBOUT + '/threading/*.cpp'))
flibObjs.extend(Glob(CPPFLIBOUT + '/os/*.cpp'))
flibObjs.extend(Glob(CPPFLIBOUT + '/os/debug/*.cpp'))
flibObjs.extend(Glob(CPPFLIBOUT + '/lang/*.cpp'))
flibObjs.extend(Glob(CPPFLIBOUT + '/convert/*.cpp'))
flibObjs.extend(Glob(CPPFLIBOUT + '/io/__internal/*.cpp'))
flibObjs.extend(Glob(CPPFLIBOUT + '/io/*.cpp'))
flibObjs.extend(Glob(CPPFLIBOUT + '/util/*.cpp'))
flibObjs.extend(Glob(CPPFLIBOUT + '/util/zip/*.cpp'))
flibObjs.extend(Glob(CPPFLIBOUT + '/net/*.cpp'))
flibObjs.extend(Glob(CPPFLIBOUT + '/net/ssl/*.cpp'))
flibObjs.extend(Glob(CPPFLIBOUT + '/net/http/*.cpp'))
flibObjs.extend(Glob(CPPFLIBOUT + '/net/http/message/*.cpp'))
flibObjs.extend(Glob(CPPFLIBOUT + '/net/http/util/*.cpp'))
flibObjs.extend(Glob(CPPFLIBOUT + '/security/cert/*.cpp'))
flibObjs.extend(Glob(CPPFLIBOUT + '/security/crypto/*.cpp'))
flibObjs.extend(Glob(CPPFLIBOUT + '/security/__internal/*.cpp'))
flibObjs.extend(Glob(CPPFLIBOUT + '/database/*.cpp'))
flibObjs.extend(Glob(CPPFLIBOUT + '/database/sqlite/*.cpp'))
flibObjs.extend(Glob(CPPFLIBOUT + '/xml/sax/__internal/*.cpp'))
flibObjs.extend(Glob(CPPFLIBOUT + '/xml/sax/*.cpp'))
flibObjs.extend(Glob(CPPFLIBOUT + '/xml/dom/*.cpp'))
flibObjs.extend(Glob(CPPFLIBOUT + '/xml/writer/*.cpp'))
flibObjs.extend(Glob(CPPFLIBOUT + '/json/__internal/*.cpp'))
flibObjs.extend(Glob(CPPFLIBOUT + '/json/*.cpp'))
flibObjs.extend(GetExtraObjectFiles())

libcppflib = BuildLibrary(cppflibEnv, CPPFLIBOUT + '/cppflib', flibObjs)

##########################
# build Test case 1
##########################
TEST_1_OUT = GetOutputDir('bin_Testcppflib')
TEST_1_SRCBASE = '../Testcppflib'

TestcppflibEnv = GetBuildingEnvironment()
TestcppflibEnv.VariantDir(TEST_1_OUT, TEST_1_SRCBASE, duplicate = 0)
TestcppflibEnv.Append(CCFLAGS = GetBuildCCFlags())
TestcppflibEnv.Append(CPPPATH = ['../cppflib'])
TestcppflibEnv.Append(LIBS = GetLinkingLibrariesForExe())
TestcppflibEnv.Append(LIBPATH = [CPPFLIBOUT])
TestcppflibEnv.Prepend(LIBPATH = GetExtraLibraryPath())
test1Objs = Glob(TEST_1_OUT + '/*.cpp')

binTestcppflib = TestcppflibEnv.Program(TEST_1_OUT + '/Testcppflib', test1Objs)

Depends(binTestcppflib, libcppflib)
if ShouldGiveWarning():
   TestcppflibEnv.AddPostAction(binTestcppflib, Action(PostBuildWarning))

if GetOption('buildso'):
   TestcppflibEnv.AddPostAction(binTestcppflib, Action(PostCopySharedObject))

##########################
# build Test case 2
##########################
TEST_2_OUT = GetOutputDir('bin_Testcppflib_2')
TEST_2_SRCBASE = '../Testcppflib_2'

TestcppflibEnv_2 = GetBuildingEnvironment()
TestcppflibEnv_2.VariantDir(TEST_2_OUT, TEST_2_SRCBASE, duplicate = 0)
TestcppflibEnv_2.Append(CCFLAGS = GetBuildCCFlags())
TestcppflibEnv_2.Append(CPPPATH = ['../cppflib'])
TestcppflibEnv_2.Append(LIBS = GetLinkingLibrariesForExe())
TestcppflibEnv_2.Append(LIBPATH = [CPPFLIBOUT])
TestcppflibEnv_2.Prepend(LIBPATH = GetExtraLibraryPath())
test2Objs = Glob(TEST_2_OUT + '/*.cpp')

binTestcppflib_2 = TestcppflibEnv_2.Program(TEST_2_OUT + '/Testcppflib_2', test2Objs)

Depends(binTestcppflib_2, libcppflib)
if GetOption('buildso'):
   TestcppflibEnv_2.AddPostAction(binTestcppflib_2, Action(PostCopySharedObject))

#############################
# extra clean action
#############################
def CleanFlagIsSet() :
    import os
    if GetOption('clean'):
        return 1
    return 0

def CleanAction(dirs=[]):
    import shutil
    if CleanFlagIsSet():
        print '>>>>>>> Cleaning directories ...'
        for d in dirs:
            shutil.rmtree(d, ignore_errors=1)

CleanAction([CPPFLIBOUT, TEST_1_OUT, TEST_2_OUT])




